import {defineComponent, nextTick, reactive, ref} from "vue";
import {Row, Col,message} from 'ant-design-vue';
import Modal from "@/components/InputFormula/components/Modal.vue";
import Tree from "@/components/InputFormula/components/Tree.vue";
import Explain from "@/components/InputFormula/components/Explain.vue";
import CodeEditor from "@/components/InputFormula/components/CodeEditor/index.vue";
import style from "./index.module.scss";
import * as formula  from 'hot-formula-parser';
const groupByNamespace = (items: any) => {
    const grouped: any = {};
    items.forEach((item: any) => {
        const {namespace} = item;
        if (!grouped[namespace]) {
            grouped[namespace] = [];
        }
        grouped[namespace].push(item);
    });
    const result: any = [];
    Object.keys(grouped).forEach((item, index) => {
        result.push({
            value: index,
            label: item,
            children: grouped[item]
        })
    })
    return result;
}
const flattenOrganizationTree = (tree: any, parentPath = '', namespace: any [] = []): void => {
    let flattenedArray: any = [];
    for (const node of tree) {
        let isShow = true
        if (namespace && namespace.length > 0) {
            if (namespace.indexOf(node.namespace) > -1) {
                isShow = false
            }
        }
        const fullPath = parentPath && !isShow ? `${parentPath}.${node.label}` : node.label;
        flattenedArray.push({...node, label: parentPath == '' ? node.label : fullPath});
        if (node.children && node.children.length > 0) {
            flattenedArray = flattenedArray.concat(flattenOrganizationTree(node.children, fullPath,namespace));
        }
    }
    return flattenedArray;
}

interface Options {
    text: string;
}

export default defineComponent({
    emits: ['confirm'],
    name: 'InputFormula',
    props: {
        modalOptions: {
            type: Object as () => Options,
            required: true,
        },
        formOptions: {
            type: Object as () => Options,
            required: true,
        },
        editorOptions:{
            type: Object as () => Options,
            required: true,
        },
        funcOptions: {
            type: Object as () => Options,
            required: true,
        },
        explainOptions: {
            type: Object as () => Options,
            default:{
                list:[]
            },
            required: true,
        }
    },
    setup: function (props, ctx) {
        //将数据扁平化，注：数据不能有重名的否则会覆盖
        function flattenObject(obj: any, parentKey = '', result: any = {}) {
            for (const key in obj) {
                if (obj.hasOwnProperty(key)) {
                    if (typeof obj[key] === 'object' && obj[key] !== null) {
                        // 递归处理嵌套对象
                        flattenObject(obj[key], key, result);
                    } else {
                        // 将属性添加到结果对象中
                        result[key] = obj[key];
                    }
                }
            }
            return result;
        }

        function convertFormulaObject(data: any, formula: string) {
            let newData = flattenObject(data);
            return formula.replace(/\{(\w+)\}/g, (_, key) => typeof newData[key] === 'string' ? `'${newData[key]}'` : newData[key]);
        }

        function RunFormula(data: any, str: string) {
            const parser = new formula.Parser();
            let result = parser.parse(convertFormulaObject(data, str));
            if (result.error != null) {
                message.warning("公式运行失败，请检查对象或公式")
                return false;
            }
            return result.result
        }

        ctx.expose({RunFormula})
        const _props = reactive({...props});
        const {slots} = ctx
        const {modalOptions, formOptions,editorOptions, funcOptions, explainOptions} = _props;
        //modal弹窗配置
        const modalParams = reactive({
            open: false,
            ...modalOptions
        })
        //左侧变量树配置
        let {treeData: formTreeData, showParentByNameSpace} = (formOptions as any)
        const formParams = reactive({
            ...formOptions,
            treeData: groupByNamespace(formTreeData)
        })
        //右侧函数树配置
        let {treeData: funcTreeData} = (funcOptions as any)
        const funcParams = reactive({
            ...funcOptions,
            treeData: groupByNamespace(funcTreeData)
        })
        //解释器配置
        const explainParams = reactive({
            ...explainOptions
        })
        //编辑器配置
        const codeEditorOptions = reactive({
            entityFields: flattenOrganizationTree(formTreeData, '', showParentByNameSpace),
            formulaFunctions: flattenOrganizationTree(funcTreeData)
        })

        const codeEditorRef = ref()

        //弹窗点击确认
        const handelOk = (e: any) => {
            let docs = codeEditorRef.value.getEditorValue()
            if (docs == '') {
                message.warning("请编辑公式内容后进行保存")
                return false
            }
            const parser = new formula.Parser();
            let result = parser.parse(docs.replace(/\{.*?\}/g, '1'));
            if (result.error != null) {
                message.warning("公式校验失败，请检查公式语法")
                return false;
            }
            modalParams.open = false
            ctx.emit('confirm', docs)
        }
        //弹窗点击取消
        const handelCancel = (e: any) => {
            modalParams.open = false
        }
        //按钮打开弹窗
        const handleDialog = () => {
            modalParams.open = true;
            nextTick(() => {
                codeEditorRef.value.setEditorValue((editorOptions as any)["docs"])
            })
        };
        //函数选中更新解释
        const handleExplain = (e: any) => {
            (explainParams as any)["list"] = e;
        }
        //代码提示回车选中
        const handleTipsKeyDown = (e: any) => {
            handleExplain(e.detail ? e.detail.split(" ; ") : undefined)
        }
        //侧边栏选中节点后
        const handleSelectNode = (type: number, event: any) => {
            switch (type) {
                case 0:
                    codeEditorRef.value.insertTag(event)
                    break;
                case 1:
                    codeEditorRef.value.insertFormula(event)
                    break;
            }
        }
        return () => (
            <>
                {slots.default &&
                    (<Row v-model:onClick={handleDialog}>
                        {slots.default?.()}
                    </Row>)
                }
                <Modal {...modalParams} onHandleOk={handelOk} onHandleCancel={handelCancel}>
                    <Row class={style['modal-content']}>
                        <Col span={5} class={style['modal-variable']}>
                            <Tree {...formParams} onHandleSelectNode={(event) => handleSelectNode(0, event)}/>
                        </Col>
                        <Col span={14} class={style['modal-editor']}>
                            <Row class={style['editor']}>
                                {/*@ts-ignore*/}
                                <CodeEditor
                                    ref={codeEditorRef}
                                    {...codeEditorOptions}
                                    onHandleTipsKeyDown={handleTipsKeyDown}
                                />
                            </Row>
                            <Row class={style['explain']}>
                                {/*@ts-ignore*/}
                                <Explain {...explainParams}/>
                            </Row>
                        </Col>
                        <Col span={5} class={style['modal-func']}>
                            <Tree {...funcParams} onHandleExplain={handleExplain}
                                  onHandleSelectNode={(event) => handleSelectNode(1, event)}/>
                        </Col>
                    </Row>
                </Modal>
            </>
        );
    }
});
